package com.katze.boot.common.utils;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.compress.archivers.*;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.io.IOUtils;
import org.apache.tomcat.util.buf.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 说  明：
 * 作  者：zf.zeng
 * 日  期：2020-06-02 (星期二)
 **/
public class FileUtils {
    private static final Logger log = LoggerFactory.getLogger(FileUtils.class);

    /**
     * 压缩Jar
     */
    public static void JARCompress(Path source, Path target) {
        try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(Files.newOutputStream(target));
             ArchiveOutputStream<ZipArchiveEntry> out = new ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.JAR, bufferedOutputStream)) {
            Iterator<File> files = org.apache.commons.io.FileUtils.iterateFiles(source.toFile(), null, true);
            while (files.hasNext()) {
                File file = files.next();
                Path path = source.relativize(file.toPath());
                ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(file, path.toString());
                out.putArchiveEntry(zipArchiveEntry);
                IOUtils.copy(Files.newInputStream(file.toPath()), out);
                out.closeArchiveEntry();
            }
            out.finish();
        } catch (IOException e) {
            log.error("创建文件失败", e);
        } catch (ArchiveException e) {
            log.error("不支持的压缩格式", e);
        }
    }

    /**
     * 解压Jar
     */
    public static void JARDecompress(Path dest, Path target) {
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(dest.toFile()));
             ArchiveInputStream<ArchiveEntry> in = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.JAR, bufferedInputStream, "UTF-8")) {
            ArchiveEntry entry;
            //循环遍历解压
            while ((entry = in.getNextEntry()) != null) {
                if (entry.isDirectory()) {
                    Files.createDirectories(target.resolve(entry.getName()));
                } else {
                    OutputStream out = org.apache.commons.io.FileUtils.openOutputStream(target.resolve(entry.getName()).toFile());
                    IOUtils.copy(in, out);
                    out.close();
                }
            }
        } catch (FileNotFoundException e) {
            log.error("未找到jar文件", e);
        } catch (ArchiveException e) {
            log.error("不支持的压缩格式", e);
        } catch (IOException e) {
            log.error("文件写入发生错误", e);
        }
    }

    /**
     * 递归循环遍历文件
     * @param path 目标目录路径
     * @param consumer 添加文件的集合
     */
    public static void walk(Path path, Consumer<? super Path> consumer) {
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
            stream.forEach(entry -> {
                if (Files.isDirectory(entry)) {
                    walk(entry, consumer);
                } else {
                    consumer.accept(entry);
                }
            });
        } catch (Exception e) {
            log.error("递归目录{}发生错误", path, e);
        }
    }

    public static String MD5(Path target) {
        try (InputStream in = new FileInputStream(target.toFile())){
            return DigestUtils.md5Hex(in);
        }catch (IOException e){
            log.error("计算出现异常：{}", e.getMessage());
            return null;
        }
    }

    public static String hmacSha1(String data, String key) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);
            byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return HexUtils.toHexString(rawHmac);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static final List<String> numbers = Arrays.asList("i", "S", "s", "m", "H", "d", "M", "y");// 自定义参数：扩展数字，需在i和S之间添加
    private static final List<String> uppers = Arrays.asList(); // 自定义参数：扩展大写字母
    private static final List<String> lowers = Arrays.asList("b"); // 自定义参数：扩展小写字母

    /**
     * 检验文件是否符合格式要求
     * 涉及不定长占位符使用时：注意不能连续性使用相同类型（数字，字母，中文）的占位符，例如：%n0%i0,字母n和i都代表数字
     * @param filename 文件名
     * @param regex 格式  %+字母+数字: 数字部分：0：不定长，>0:定长
     *              例如：SMS_%n6_JF_%n2%y2%M2%d2%H2%m2%s2%A3_%i3_%b3.%l2%L1
     *              %y: 年份, %M: 月份, %d: 日期, %H: 小时, %m: 分钟, %s: 秒, %S: 毫秒, %i: 序号
     *              %n: 表示数字,%l: 表示小写字母,%L: 表示大写字母,%t: 忽略字母或数字以及符号
     *              %其他字母： 均为扩展参数由后期业务使用时定义具体含义，存在顺序先后
     * @return 文件名中日期及序号信息，格式：日期 + 序号（8位数不足补零）
     */
    public static String verifyFilename(String filename, String regex){
        filename = filename.trim();
        String repair = regex.replaceAll("%[A-Za-z][0-9]+", "%s");
        Matcher matcher = Pattern.compile("%[A-Za-z][0-9]+").matcher(regex);
        int start = 0, end, startIndex = 0, endIndex;
        String raiser;
        Map<String, String> map = new HashMap<>();
        while (matcher.find()) {
            end = matcher.start();
            startIndex += regex.substring(start, end).length();
            start = matcher.end();
            int number = Integer.parseInt(matcher.group().substring(2));
            if(number == 0){
                endIndex = startIndex;
                String indexRegex = getRegex(filename.substring(startIndex, startIndex+1));
                String[] items = filename.split("");
                for (int i = startIndex; i < items.length; i++) if(items[i].matches(indexRegex)) endIndex++; else break;
            }else{
                endIndex = startIndex + number;
            }
            if(filename.length() < endIndex){
                map.clear();
                break;
            }else {
                String letter = matcher.group().substring(1, 2);
                if(numbers.contains(letter) || uppers.contains(letter) || lowers.contains(letter)) map.put(letter, filename.substring(startIndex, endIndex));
                startIndex = endIndex;
                raiser = (letter.equals("t")? "[A-Za-z0-9_-]"
                        :letter.equals("n") || numbers.contains(letter)?"[0-9]"
                        :letter.equals("l") || lowers.contains(letter)? "[a-z]"
                        :letter.equals("L") || uppers.contains(letter)? "[A-Z]"
                        :"[A-Za-z0-9\\\\u4e00-\\\\u9fa5]") + (number == 0? "{1,}":"{" + number + "}");
                repair = repair.replaceFirst("%s", raiser); //自定义规则转换正则表达式
            }
        }
        List<String> data = new ArrayList<>();
        if(filename.matches(repair)){
            // 数字固定部分（日期时间，序号）
            for (String item : numbers) {
                String value = map.getOrDefault(item, item.equals("S") ? "":item.equals("i")? "00000000":item.matches("[Md]")?"00":"00");
                if(item.equals("i")){
                    value = String.format("%08d", Integer.parseInt(value.length()>8? value.substring(value.length()-8):value));//序号固定8位数，超过则取最后8位数
                } else if(item.equals("y")){
                    value = value.length()>2? value.substring(value.length() - 2):value; // 只保留最后两位年
                } else {
                    value = value.length() == 1? String.format("%02d", Integer.parseInt(value)):value;
                }
                data.add(0, value);
                map.remove(item);
            }
            // 字母和数字 按需求添加，有顺序先后
            List<String> other = map.entrySet().stream().sorted(Comparator.comparing(item -> ((int)item.getKey().getBytes()[0]))).map(Map.Entry::getValue).collect(Collectors.toList());
            data.addAll(data.size()-1, other);
        }
        return String.join("", data);
    }

    private static String getRegex(String str){
        if(str.matches("[0-9]+")){
            return "[0-9]+";
        }else if(str.matches("[a-z]+")){
            return "[a-z]+";
        }else if(str.matches("[A-Z]+")){
            return "[A-Z]+";
        }else {
            return "[\\u4e00-\\u9fa5]+";
        }
    }

    public static void main(String[] ages) throws IOException {

//        String basePath = "E:\\WorkSpace\\eop\\ieop_patch\\zengzf_25401_EOP-福建-swagger文件规范性检查_20200602\\3_JavaEE_CLASS\\";
//        String targetBasePath = "E:\\3_EOP研发\\9_CODE(代码)\\1_trunk\\3_JavaEE\\maven\\ieop\\src\\main\\";
//
//        String projectName = "ieop";
//
//        String dpath = basePath.concat(projectName);
//        String spath = targetBasePath.concat("webapp");
//        FileUtils.copy(dpath, spath);
//
//        dpath = dpath.concat("\\WEB-INF\\classes");
//        spath = targetBasePath.concat("java");
//        String cccpath = "E:\\WorkSpace\\eop\\ieop\\target\\ieop\\WEB-INF\\classes";
//        copyClassFile(dpath, spath, cccpath);

        String filename = "SMS_591055_JF_20201014150000_469.dat";
//        System.out.println(filename.replaceAll("[_-]", ""));

        String n = "SMS_\\d{6}_JF_\\d{4}\\d{2}\\d{2}\\d{2}\\d{2}\\d{2}_\\d{3}.dat";
        n = "SMS_%n6_JF_%n2%a12_%n3.bat.%i2";
        n = "SMS_%n6_JF_%t2%y2%M2%d2%H2%m2%s2%S3%i0%b0.bat";
        n= "SMS_%n6_JF_20%y2%M2%d2%H2%m2%s2_%i0.dat";
        n = "SMS_%n6_JF_20%y2%M2%d2%H2%m2%s2_%i0.dat";
//        n = "我MS_%n6_JF_%n17%i0%t3.bat";
//        n = "SMS_591055_JF_20190912061018119999988tyz.bat";
        System.out.println(n);
        System.out.println(verifyFilename(filename, n));
//        System.out.println(FileMD5(Paths.get("C:\\Users\\ASUS\\Downloads\\SMS_591055_JF_20190912061018_960.dat")));
    }
}
